﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.ServiceModel;
using ClientModel.CallbackService;
using LocalServerApp;

namespace ClientModel
{
    #region Interfaces for observer pattern to GUI update
    public interface IMainWindowObserver
    {
        void notify();
    }

    public interface ISubject
    {
        void setWindow(IMainWindowObserver window);
        void notifyObserver();
    }
    #endregion


    [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
    public class Service : ILocalServerServiceCallback, ISubject
    {
        InstanceContext context;
        LocalServerServiceClient client;
        private IMainWindowObserver mainWindow;

        #region Singleton constructor
        public static Service service;
        private Service()
        {
            context = new InstanceContext(this);
            client = new LocalServerServiceClient(context);
        }

        public static Service getInstance()
        {
            if (service == null)
            {
                service = new Service();
            }
            return service;
        }
        #endregion

        #region methods

        /// <summary>
        /// Ends the current active session
        /// </summary>
        public void endSession()
        {
            try
            {
                client.endSession();
                client.unsubscribe();
            }
            catch (Exception ex)
            {
            }
        }

        /// <summary>
        /// Cancel a deposit
        /// </summary>
        public void cancelDeposit(int hangerNo)
        {
            try
            {
                client.cancelDepositClothes(hangerNo);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Starts a new session
        /// </summary>
        public void startSession()
        {
            try
            {
                client.startSession();
                client.subscribe();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public List<Hanger> getForgottenClothes()
        {
            List<Hanger> hangers;
           try
           {
                hangers = client.getForgottenClothes().ToList();
           }
           catch (System.Exception ex)
           {
           	throw ex;
           }
           return hangers;
        }

        /// <summary>
        /// Deposit clothes for a person and returns a generated hanger number 
        /// </summary>
        /// <param name="p">Person</param>
        /// <returns>Hanger number</returns>
        public int depositClothes(Person p)
        {
            try
            {
                return client.depositClothes(p.Name, p.Adress, p.Zipcode, HashMeSHA512(p.HashIdentifier), p.Male, p.Birthday);
            }
            catch (FaultException ee)
            {
                throw ee;
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Returns true if the given cpr exists in the database.
        /// </summary>
        /// <param name="cpr">cpr</param>
        /// <returns>boolean</returns>
        public bool isInWardrobe(string cpr)
        {
            if(cpr.Length < 11)
                return client.isInWardrobe(HashMeSHA512(cpr));
            else
                return client.isInWardrobe(cpr);
        }

        /// <summary>
        /// Withdraws the clothes to the specified hanger number
        /// </summary>
        /// <param name="hangerNo">hanger number</param>
        public void withdrawClothes(int hangerNo)
        {
            client.withdrawClothes(hangerNo);
        }

        /// <summary>
        /// Returns a list of clothes for the given identifier
        /// </summary>
        /// <param name="hashedIdentifier">hashed cpr</param>
        /// <returns>list of hanger numbers</returns>
        public int[] getClothesInWardrobe(string hashedIdentifier)
        {
            if(hashedIdentifier.Length < 11)
                return client.getClothesFromPerson(HashMeSHA512(hashedIdentifier));
            else
                return client.getClothesFromPerson(hashedIdentifier);
        }

        /// <summary>
        /// Returns a list of persons who matches the given params
        /// </summary>
        /// <param name="name">name</param>
        /// <param name="CPR">cpr</param>
        /// <param name="adress">adress</param>
        /// <param name="zipcode">zipcode</param>
        /// <param name="hangerNo">Hanger number</param>
        /// <returns>List of persons</returns>
        public List<Person> searchPersonsInWardrobe(string name, string CPR, string adress, string zipcode, string hangerNo)
        {
            if (CPR.Length != 0)
                CPR = HashMeSHA512(CPR);
     
            return client.searchPersonsInWardrobe(name, CPR, adress, zipcode, hangerNo).ToList();
        }

        /// <summary>
        /// Returns a SHA512 hashed string of the given.
        /// </summary>
        /// <param name="tekst">text</param>
        /// <returns>hashed string</returns>
        private string HashMeSHA512(string tekst)
        {
            System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();

            byte[] data = encoding.GetBytes(tekst);
            byte[] result;
            SHA512 shaM = new SHA512Managed();
            result = shaM.ComputeHash(data);
            return Convert.ToBase64String(result);
        }

        #endregion

        #region Callback from server on 'end session'

        public void OnEndSession()
        {
            notifyObserver();
        }

        #endregion

        #region Observer Pattern for GUI update

        public void setWindow(IMainWindowObserver window)
        {
            mainWindow = window;
        }

        public void notifyObserver()
        {
            mainWindow.notify();
        }

        #endregion


        public Boolean isLicenseValid()
        {
            Boolean isLicenseValid = client.isLicensValid();
            return isLicenseValid;
        }

        public void setLastConnectionDate(DateTime date)
        {
            client.setLastRemoteConnectionDate(date);
        }
        
        public void setNumberOfHangers(int numberOfHangers)
        {
            client.setNumberOfHangers(numberOfHangers);
        }

        public int getNumberOfHangers()
        {
            return client.getNumberOfHangers();
        }

        public DateTime getLastRemoteConnection()
        {
            return client.getLastRemoteConnectionDate();
        }

    }
}
